package com.xiashitech.agent.service;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.base.Strings;
import com.xiashitech.agent.config.StartConfig;
import com.xiashitech.agent.constants.ErrorCode;
import com.xiashitech.agent.constants.Instrumentation;
import com.xiashitech.agent.instrumentation.base.context.XSBaggage;
import com.xiashitech.agent.instrumentation.base.context.XSContext;
import com.xiashitech.agent.instrumentation.db.statement.sanitizer.SanitizerFactory;
import com.xiashitech.agent.instrumentation.db.statement.sanitizer.impl.XSSqlLocalSanitizer;
import com.xiashitech.agent.instrumentation.db.statement.sanitizer.impl.XSSqlSanitizer;
import com.xiashitech.agent.instrumentation.log.XSLevel;
import com.xiashitech.agent.utils.DurationPrinter;
import com.xiashitech.agent.utils.JsonUtil;
import com.xiashitech.agent.utils.XSMapUtil;
import com.xiashitech.interfaces.agent.dto.AgentConfigDTO;
import com.xiashitech.interfaces.apiservice.UserTracingMsg;
import com.xiashitech.interfaces.apiservice.dto.AuthFactRequest;
import com.xiashitech.interfaces.apiservice.dto.AuthFactResponse;
import com.xiashitech.interfaces.apiservice.dto.base.DB;
import com.xiashitech.sqlparser.sqlvisitor.Constants.ParserConstant;
import io.opentelemetry.javaagent.bootstrap.jdbc.DbInfo;
import io.opentelemetry.javaagent.shaded.instrumentation.jdbc.internal.DbRequest;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.context.Context;
import org.apache.struts2.ServletActionContext;
import org.jetbrains.annotations.NotNull;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.logging.Logger;

public class UserTracingService {
    private static final Logger logger = Logger.getLogger(XSSqlSanitizer.class.getName());
    //private static final XSSqlSanitizer sanitizer = new XSSqlSanitizer();

    @NotNull
    public static <REQUEST> Map<String,Object> supplementUserTracing(Baggage baggage, Context context, REQUEST request) {
        String dbName = "";
        DB db = new DB();
        Map<String,Object> attributeMap = new HashMap();
        try {
            DurationPrinter durationPrinter = new DurationPrinter("supplementUserTracing");
            if(request instanceof DbRequest) {
                DbInfo dbInfo = ((DbRequest) request).getDbInfo();
                if(dbInfo != null) {
                    dbName = dbInfo.getName() == null ? dbInfo.getDb() : dbInfo.getName();
                    if(Instrumentation.Db_Type_Mysql.equalsIgnoreCase(dbInfo.getSystem())) {
                        db.setSchema(dbName);
                    } else {
                        db.setDatabase(dbName);
                    }
                    db.setSql(((DbRequest) request).getStatement());
                    if(dbInfo != null) {
                        db.setIp(dbInfo.getHost());
                        db.setPort(String.valueOf(dbInfo.getPort()));
                        db.setType(dbInfo.getSystem());
                    }
                }
            }
            durationPrinter.printElapseDuration();
            attributeMap.put("xs.userId",baggage.getEntryValue("xs.userId") == null ? "":baggage.getEntryValue("xs.userId"));
            attributeMap.put("xs.loginName",baggage.getEntryValue("xs.loginName") == null ? "":baggage.getEntryValue("xs.loginName"));
            attributeMap.put("xs.sessionId",baggage.getEntryValue("xs.sessionId") == null ? "":baggage.getEntryValue("xs.sessionId"));
            attributeMap.put("xs.requestDate",baggage.getEntryValue("xs.requestDate") == null ? "":baggage.getEntryValue("xs.requestDate"));
            AuthFactRequest authFactRequest = new AuthFactRequest();
            authFactRequest.setSystemId(StartConfig.getParamValueFromKey(StartConfig.systemId));
            authFactRequest.setSourceSystemId(baggage.getEntryValue("xsAuthSystemId") == null ? StartConfig.getParamValueFromKey(StartConfig.systemId):baggage.getEntryValue("xsAuthSystemId"));
            AgentConfigDTO agentConfig = AgentConfigDTO.getConfig();
            if(agentConfig == null || agentConfig.getSystemAppCode() == null) {
                throw new RuntimeException();
            }
            authFactRequest.setAppCode(agentConfig.getSystemAppCode().get(StartConfig.getParamValueFromKey(StartConfig.systemId)));
            authFactRequest.setUserId(attributeMap.get("xs.userId").toString());
            if(agentConfig == null || agentConfig.getSystemSign() == null) {
                throw new RuntimeException();
            }
            authFactRequest.setSign(agentConfig.getSystemSign().get(StartConfig.getParamValueFromKey(StartConfig.systemId)));
            authFactRequest.setDb(db);
            //添加数据出口api的api名称和api所在系统
            String endpointApi = baggage.getEntryValue("endpoint-api");
            String apiSystemId = baggage.getEntryValue("api-systemId");
            System.out.println(Thread.currentThread().getId() + " DesenseDebug, UserTracingService supplementUserTracing userId: " + attributeMap.get("xs.userId").toString() + ", endpointApi: " + endpointApi);
            authFactRequest.setApiName(endpointApi);
            authFactRequest.setApiSystemId(apiSystemId);
            durationPrinter.printElapseDuration();
            //AuthFactResponse authFactResponse = sanitizer.supplementUserTracing(authFactRequest);
            AuthFactResponse authFactResponse = SanitizerFactory.create().supplementUserTracing(authFactRequest);
            durationPrinter.printElapseDuration();
            if(authFactResponse == null) {
                attributeMap.put("xs.db.name",dbName);
                attributeMap.put("xs.db.operate","");
                attributeMap.put("xs.db.tables.auth", "");
                attributeMap.put("xs.db.metadataConfig",false);
                durationPrinter.printElapseDuration();
            } else {
                attributeMap.put("xs.db.name",dbName);
                attributeMap.put("xs.db.operate",authFactResponse.getOperate());
                attributeMap.put("xs.db.tables.auth", JsonUtil.convertObjectToString(authFactResponse.getTables()));
                if(ErrorCode.RESULT_4009.getCode().equals(authFactResponse.getCode())) {
                    attributeMap.put("xs.db.metadataConfig",false);
                } else {
                    attributeMap.put("xs.db.metadataConfig",true);
                }
                /**
                 * 设置tracing返回结果，在api脱敏时使用
                 */
//                //本地测试代码
//                authFactResponse.getTables().forEach(table -> table.getColumns().forEach(column -> {
//                    if (column.getColName().equals("name")) {
//                        column.setColAuth(false);
//                        column.setColAuthReason("black");
//                    }
//                    if (column.getColName().equals("serial_no")) {
//                        column.setColAuth(false);
//                        column.setColAuthReason("black");
//                    }
//                }));
                durationPrinter.printElapseDuration();
                StringJoiner stringJoiner = new StringJoiner("&");
                if(endpointApi != null) {
                    stringJoiner.add(endpointApi);
                    authFactResponse.getTables().forEach(table -> table.getColumns().forEach(column -> {
                        if(column.getAuthDesenseActions() != null) {
                            column.getAuthDesenseActions().forEach(authDesenseAction -> {
                                System.out.println(Thread.currentThread().getId() + " DesenseDebug, UserTracingService supplementUserTracing authFactResponse endpointApi: " + endpointApi + ", authDesenseAction: " + (authDesenseAction==null?"NULL":(authDesenseAction.getApiPath()+":"+authDesenseAction.getPatchPath()+":"+authDesenseAction.getSystemId().equals(apiSystemId))) + ", colAuth: " + column.getColAuth()  + ", colAuthReason: " + column.getColAuthReason());
                                if(endpointApi != null && authDesenseAction != null && endpointApi.contains(authDesenseAction.getApiPath()) && authDesenseAction.getSystemId().equals(apiSystemId) && !column.getColAuth() && column.getColAuthReason() != null && !"none".equals(column.getColAuthReason())) {
                                    stringJoiner.add(authDesenseAction.getPatchPath() + "=" + authDesenseAction.getIsExecute() + "," + authDesenseAction.getRegular() + "," + authDesenseAction.getReplaceSymbol() + "," + authDesenseAction.getSystemId());
                                }
                            });
                        }
                    }));
                    if(stringJoiner.toString().split("&").length > 1) {
                        XSBaggage xsBaggage = XSBaggage.fromContext(XSContext.current());
                        String endpointApiBack = xsBaggage.getEntryValue("endpoint-api-back");
                        if(endpointApiBack != null && !endpointApiBack.isEmpty()) {
                            String newBack = stringJoiner.toString();
                            if(!endpointApiBack.contains(newBack)) {// 简单消重
                                endpointApiBack += "#" + newBack;
                            } else {
                                System.out.println(Thread.currentThread().getId() + " DesenseDebug, UserTracingService supplementUserTracing endpointApiBack RemoveDup endpointApi: " + endpointApi + ", endpointApiBack: " + endpointApiBack + ", newBack: " + newBack);
                            }
                        } else {
                            endpointApiBack = stringJoiner.toString();
                        }
                        XSBaggage.fromContext(XSContext.current()).toBuilder().put("endpoint-api-back",endpointApiBack).build().storeInContext(XSContext.current()).makeCurrent();
                    }
                }
                System.out.println(Thread.currentThread().getId() + " DesenseDebug, UserTracingService supplementUserTracing userId: " + attributeMap.get("xs.userId") + ", endpoint-api-back: " + stringJoiner);
                durationPrinter.printElapseDuration();
            }
            if (StrUtil.isEmpty(XSMapUtil.getStr(attributeMap, "xs.db.operate"))) { //MapUtil.getStr(attributeMap, "xs.db.operate"))){
                extractAndReSetSQLType(db.getSql(), attributeMap);
            }
            durationPrinter.printElapseDuration();
        } catch (Throwable e) {
            e.printStackTrace();
            StartConfig.throwableStackPrinter(e);
            logger.log(XSLevel.DEBUG, "The Agent fails to obtain database permission information from Agentservice.");
        }
        return attributeMap;
    }

    // 修复BUG http://***/bugs/view?bug_id=1144098157001000324
    // 当鉴权出参对象为空或其他因素导致触发此 bug 的时候，通过这里简单字符串匹配，识别SQLType。（调用SQL解析比较费时，这个场景简单字符串匹配识别SQLType即可）
    private static void extractAndReSetSQLType(String sql, Map<String,Object> attributeMap){
        if (StrUtil.startWithIgnoreCase(sql, ParserConstant.SELECT)){
            attributeMap.put("xs.db.operate",ParserConstant.SELECT);
        } else if (StrUtil.startWithIgnoreCase(sql, ParserConstant.UPDATE)) {
            attributeMap.put("xs.db.operate",ParserConstant.UPDATE);
        } else if (StrUtil.startWithIgnoreCase(sql, ParserConstant.DELETE)) {
            attributeMap.put("xs.db.operate",ParserConstant.DELETE);
        } else if (StrUtil.startWithIgnoreCase(sql, ParserConstant.INSERT)) {
            attributeMap.put("xs.db.operate",ParserConstant.INSERT);
        }
    }
}
